﻿namespace ScrumFactory.Data.Sql
{
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Data.Objects;
    using System.Data.EntityClient;
    using System.Transactions;
    using System.Linq;

    [Export(typeof(IBacklogRepository))]
    public class SqlBacklogRepository : IBacklogRepository
    {
        private string connectionString;

        [ImportingConstructor()]
        public SqlBacklogRepository(
            [Import("ScrumFactoryEntitiesConnectionString")]
            string connectionString)
        {
            this.connectionString = connectionString;
        }

        /// <summary>
        /// Gets the entire backlog of a project from the database and its lasted planned hours.
        /// </summary>
        /// <param name="projectUId">The projectUId</param>
        /// <returns>A list of backlog items</returns>
        public ICollection<ScrumFactory.BacklogItem> GetCurrentBacklog(string projectUId)
        {
            using (var context = new ScrumFactoryEntities(this.connectionString))
            {
                
                // get all items from the backlog with the lasted planned hours
                var itemsWithHours =
                    from i in context.BacklogItems
                    where i.ProjectUId == projectUId
                    let maxPlanningNumber = i.PlannedHours.Max(h => h.PlanningNumber)
                    select new { BacklogItem = i, PlannedHours = i.PlannedHours.Where(ih => ih.PlanningNumber == maxPlanningNumber) };
   
                
                return itemsWithHours.AsEnumerable().Select(i => i.BacklogItem).ToList<BacklogItem>();
            }
        }

        /// <summary>
        /// Gets the entire backlog of a project from the database and its all its planned hours.
        /// </summary>
        /// <param name="projectUId">The projectUId</param>
        /// <returns>A list of backlog items</returns>
        public ICollection<ScrumFactory.BacklogItem> GetBacklog(string projectUId) {
            using (var context = new ScrumFactoryEntities(this.connectionString)) {
                return context.BacklogItems.Include("PlannedHours").Where(i => i.ProjectUId == projectUId).ToList();
            }
        }

        
        
        /// <summary>
        /// Gets a backlog item from the database and its lasted planned hours.
        /// </summary>
        /// <param name="backlogItemUId">The backlogitemUid</param>
        /// <returns>The backlog item</returns>
        public ScrumFactory.BacklogItem GetBacklogItem(string backlogItemUId) {
            using (var context = new ScrumFactoryEntities(this.connectionString)) {


                var itemWithHours =
                        from i in context.BacklogItems
                        where i.BacklogItemUId == backlogItemUId
                        let maxPlanningNumber = i.PlannedHours.Max(h => h.PlanningNumber)
                        select new { BacklogItem = i, PlannedHours = i.PlannedHours.Where(ih => ih.PlanningNumber == maxPlanningNumber) };

                return itemWithHours.AsEnumerable().Select(i => i.BacklogItem).SingleOrDefault<BacklogItem>();

            }

            

        }
      
        /// <summary>
        /// 
        /// </summary>
        /// <param name="backlogItemId"></param>
        public void DeleteBacklogItem(string backlogItemId)
        {
            using (var context = new ScrumFactoryEntities(this.connectionString))
            {
                var item = context.BacklogItems.SingleOrDefault(b => b.BacklogItemUId == backlogItemId);

                if (item != null)
                {
                    context.DeleteObject(item);
                    context.SaveChanges();
                }
            }
        }

        /// <summary>
        /// Saves a backlog item and its hours.
        /// </summary>
        /// <param name="item">The item to be saved</param>
        public void SaveBacklogItem(BacklogItem item) {
            SaveBacklogItem(item, true);
        }

        /// <summary>
        /// Saves a backlog item, but ignores it hours.
        /// </summary>
        /// <param name="item">The item to be saved</param>
        public void SaveBacklogItemIgnoreHours(BacklogItem item) {
            SaveBacklogItem(item, false);
        }

        private void SaveBacklogItem(BacklogItem item, bool saveHours) {
            using (var context = new ScrumFactoryEntities(this.connectionString)) {
                BacklogItem oldItem = GetBacklogItem(item.BacklogItemUId);

                // if is a new item insert it
                if (oldItem == null) {
                    context.BacklogItems.AddObject(item);

                // updates the item
                } else {

                    
                    // updates the item
                    context.AttachTo("BacklogItems", oldItem);
                    context.ApplyCurrentValues<BacklogItem>("BacklogItems", item);

                    if (saveHours) {

                        // detect the changes
                        var insertedHours = item.PlannedHours.Where(p => !oldItem.PlannedHours.Any(o => (o.BacklogItemUId == p.BacklogItemUId && o.RoleUId == p.RoleUId && o.PlanningNumber == p.PlanningNumber))).ToList();

                        // ATTENTION HERE: DID NOT INCLUDE THE o.PlanningNumber == p.PlanningNumber BECAUSE I DONT WANT TO DELETE OLD PLANNINGS
                        var deletedHours = oldItem.PlannedHours.Where(o => !item.PlannedHours.Any(p => o.BacklogItemUId == p.BacklogItemUId && o.RoleUId == p.RoleUId)).ToList();

                        var updatedHours = item.PlannedHours.Where(p => oldItem.PlannedHours.Any(o => o.BacklogItemUId == p.BacklogItemUId && o.RoleUId == p.RoleUId && o.PlanningNumber == p.PlanningNumber)).ToList();

                        // insert, update and delete
                        foreach (PlannedHour p in updatedHours)
                            context.ApplyCurrentValues<PlannedHour>("PlannedHours", p);
                        foreach (PlannedHour p in insertedHours)
                            context.AddObject("PlannedHours", p);
                        foreach (PlannedHour p in deletedHours)
                            context.DeleteObject(p);

                    }

                    
                }

                context.SaveChanges();
            }
        }
    }
}
